﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.ComponentModel;
using MacomberMapSystem.Common.Database;
using System.Drawing.Imaging;
using MacomberMapSystem.Common.Serialization;
using MacomberMapSystem.Common.CIM;
using System.IO;
using MacomberMapSystem.Common.Integration;
using System.Windows.Forms;
using System.Data.Common;
using MacomberMapSystem.Common.Exports;
using MacomberMapSystem.Common.Internals;
using System.Data;
using System.Drawing;
using MacomberMapSystem.Common.User_Interfaces.One_Lines;
using MacomberMapSystem.Common.User_Interfaces;
using System.Threading;
using MacomberMapSystem.Common.Properties;
using Ionic.Zip;
using System.Collections.Concurrent;
#if DotNetOracle
using System.Data.OracleClient;
#else
    using Oracle.DataAccess.Client;
#endif

namespace MacomberMapSystem.Common.Processing
{
    /// <summary>
    /// This class provides for the validation of one-lines
    /// </summary>
    public class CIM_OneLineValidator : CIM_Processor
    {        
        #region Variable declarations
        /// <summary>Whether the substation validation email has been sent out</summary>
        private bool SentSubstationValidation = false;

        /// <summary>Our collection of special one-lines</summary>
        public static MM_Database_OneLine[] SpecialOneLines = new MM_Database_OneLine[] { new MM_Database_OneLine(MM_DDL_Exporter.WriteRASTabularDisplay) };

        /// <summary>The path in which the one-line JPEGs should be written</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue), Category("Output"), Description("The path in which the one-line images should be written")]
        public String OLImageDirectory
        {
            get { return _OLImageDirectory; }
            set { _OLImageDirectory = value; }
        }
        private String _OLImageDirectory;


        /// <summary>The format of the one-lines</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue), Category("Output"), Description("The format of the one-lines"), DefaultValue(typeof(ImageFormat), "Png")]
        public ImageFormat OneLineImageFormat
        {
            get { return _OneLineImageFormat; }
            set { _OneLineImageFormat = value; }
        }
        private ImageFormat _OneLineImageFormat = ImageFormat.Png;


        private String _DDLDirectory;

        /// <summary>The path where the DDL should be written</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue), Category("Output"), Description("The path where the DDLs should be written")]
        public String DDLDirectory
        {
            get { return _DDLDirectory; }
            set { _DDLDirectory = value; }
        }


        private String _MMXmlDirectory;

        /// <summary>The path where the DDL should be written</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue), Category("Output"), Description("The path where the Macomber Map XML One-Lines should be written")]
        public String MMXmlDirectory
        {
            get { return _MMXmlDirectory; }
            set { _MMXmlDirectory = value; }
        }


        private String _EMSDirectory;

        /// <summary>The path where the EMS FGBuilder program resides</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue), Category("Output"), Description("The path where the EMS FGBuilder program resides")]
        public String EMSDirectory
        {
            get { return _EMSDirectory; }
            set { _EMSDirectory = value; }
        }

        /// <summary>The path where the EMS FGBuilder images reside</summary>
        [MM_Serializable_Target(MM_Serializable_Target.enumTargetType.FieldValue), Category("Output"), Description("The path where the EMS FGBuilder images reside")]
        public String FGImageDirectory
        {
            get { return _FGImageDirectory; }
            set { _FGImageDirectory = value; }
        }
        private String _FGImageDirectory;

        /// <summary>The collection of substations to be validated and exported</summary>
        public ConcurrentQueue<CIM_Element> SubstationsToParse = new ConcurrentQueue<CIM_Element>();

        /// <summary>The collection of breaker-to-breaker traces to be validated and exported</summary>
        public ConcurrentQueue<CIM_Trace> TracesToParse = new ConcurrentQueue<CIM_Trace>();

        /// <summary>The list of threads to handle processing</summary>
        public List<Thread> ProcessingThreads = new List<Thread>();

        /// <summary>The thread for handling our breaker to breaker exports</summary>
        public Thread TraceProcessorThread = null;
        
        /// <summary>Our thread for one-line compilation</summary>
        public Thread OneLineCompilation;

        /// <summary>Our collection of one-lines to be compiled</summary>
        public ConcurrentQueue<MM_Database_OneLine> OneLinesToCompile = new ConcurrentQueue<MM_Database_OneLine>();
        #endregion


        /// <summary>
        /// 
        /// </summary>
        /// <param name="xElem"></param>
        /// <param name="NamedParameters"></param>
        /// <param name="Repository"></param>
        public CIM_OneLineValidator(XmlElement xElem, Dictionary<String, Object> NamedParameters, MM_Repository Repository)
            : base(xElem, NamedParameters, Repository)
        {
            //First, build our list of substations to be processed
            if (NamedParameters.ContainsKey("Model"))
            {
                MM_Database_Model Model = NamedParameters["Model"] as MM_Database_Model;
                this.Total = Model.Substations.Count;
                this.Current = 0;
            }

        }

        /// <summary>
        /// Provide the name of our process
        /// </summary>
        public override string ProcessName
        {
            get { return "One-Line Validator"; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="Command"></param>
        /// <param name="CurModel"></param>
        /// <param name="TargetDirectory"></param>
        /// <param name="DDLDirectory"></param>
        /// <param name="ExportDirectory"></param>
        /// <param name="OLImageDirectory"></param>
        /// <param name="EMSDirectory"></param>
        /// <param name="ImageDirectory"></param>
        /// <param name="Db"></param>
        /// <param name="Repository"></param>
        /// <param name="EmailResults"></param>
        /// <param name="MailRecipients"></param>
        /// <param name="EmailSubject"></param>
        /// <param name="SMTPServer"></param>
        /// <param name="MMXmlDirectory"></param>
        /// <param name="ProcessLog"></param>
        /// <param name="SendEmail"></param>
        public static void ExportOneLine(String Command, MM_Database_Model CurModel, String TargetDirectory, String DDLDirectory, String ExportDirectory, String OLImageDirectory, String EMSDirectory, String ImageDirectory, MM_Database_Connector Db, MM_Repository Repository, bool EmailResults, String SMTPServer, String EmailSubject, MM_String_Collection MailRecipients, String MMXmlDirectory, StringBuilder ProcessLog, bool SendEmail)
        {
            //clear the log to prepare for the new email
            ProcessLog.Clear();

            //Make sure the directories exist
            if (!Directory.Exists(TargetDirectory))
                Directory.CreateDirectory(TargetDirectory);
            if (!Directory.Exists(DDLDirectory))
                Directory.CreateDirectory(DDLDirectory);
            if (!Directory.Exists(ExportDirectory))
                Directory.CreateDirectory(ExportDirectory);
            if (!Directory.Exists(OLImageDirectory))
                Directory.CreateDirectory(OLImageDirectory);
            if (!String.IsNullOrEmpty(MMXmlDirectory) && !Directory.Exists(MMXmlDirectory))
                Directory.CreateDirectory(MMXmlDirectory);
            //Make sure our target eSet is there
            if (!File.Exists(Path.Combine(DDLDirectory, "ercotonelines_eset.ddl")))
                using (StreamWriter sESET = new StreamWriter(Path.Combine(DDLDirectory, "ercotonelines_eset.ddl"), false, Encoding.ASCII))
                    sESET.Write(Data_Manager.WriteColorsAndModelSpecifics(Resources.ERCOTOneLines_ESet, CurModel));

            List<CIM_Element> ElementsToProcess = new List<CIM_Element>();
            List<CIM_Element> OneLinesToCompile = new List<CIM_Element>();

            if (Command.Equals("Completed", StringComparison.CurrentCultureIgnoreCase))
                using (OracleCommand oCmd = new OracleCommand("SELECT ONELINE FROM MM_DATABASE_ASSIGNMENT WHERE MODEL=" + CurModel.ID.ToString() + " AND COMPLETEDON IS NOT NULL", Db.Database as OracleConnection))
                using (OracleDataReader oRd = oCmd.ExecuteReader())
                    while (oRd.Read())
                        using (OracleCommand oCmd2 = new OracleCommand("SELECT RDFID FROM MM_DATABASE_ONELINE WHERE ID=" + oRd["ONELINE"].ToString(), Db.Database as OracleConnection))
                            ElementsToProcess.Add(CurModel.FromRdfID(oCmd2.ExecuteScalar().ToString()));
            else if (Command.Equals("Missing", StringComparison.CurrentCultureIgnoreCase))
            {               
                foreach (CIM_Element SubElem in CurModel.ElementsOfType("cim:Substation"))
                    if (bool.Parse(SubElem["etx:Substation.forNetwork"]) && bool.Parse(SubElem["etx:Substation.forScada"]))
                        if (!File.Exists(Path.Combine(DDLDirectory, "NETMOM-" + SubElem.Name + "_Dset.ddl")) || !File.Exists(Path.Combine(DDLDirectory, "SCADA-" + SubElem.Name + "_Dset.ddl")) || !File.Exists(Path.Combine(DDLDirectory, "OTS-" + SubElem.Name + "_Dset.ddl")) || !File.Exists(Path.Combine(OLImageDirectory, SubElem.Name + ".png")) || !File.Exists(Path.Combine(MMXmlDirectory, SubElem.Name + ".MM_OneLine")))                            
                            ElementsToProcess.Add(SubElem);
                        else if (!File.Exists(Path.Combine(ExportDirectory, "rtnet\\" + SubElem.Name + ".fgdisplay")) || !File.Exists(Path.Combine(ExportDirectory, "scada\\" + SubElem.Name + ".fgdisplay")) || !File.Exists(Path.Combine(ExportDirectory, "dtspsm\\" + SubElem.Name + ".fgdisplay")))
                            OneLinesToCompile.Add(SubElem);
                foreach (CIM_Trace Trace in CurModel.Traces.Values)
                    if (!File.Exists(Path.Combine(DDLDirectory, "NETMOM-__" + Trace.Name + "_Dset.ddl")) || !File.Exists(Path.Combine(DDLDirectory, "SCADA-__" + Trace.Name + "_Dset.ddl")) || !File.Exists(Path.Combine(DDLDirectory, "OTS-__" + Trace.Name + "_Dset.ddl")) || !File.Exists(Path.Combine(OLImageDirectory,"__"+ Trace.Name + ".png")) || !File.Exists(Path.Combine(MMXmlDirectory, "__"+Trace.Name + ".MM_OneLine")))                             
                        ElementsToProcess.Add(Trace);
                    else if (!File.Exists(Path.Combine(ExportDirectory, "rtnet\\__" + Trace.Name + ".fgdisplay")) || !File.Exists(Path.Combine(ExportDirectory, "scada\\__" + Trace.Name + ".fgdisplay")) || !File.Exists(Path.Combine(ExportDirectory, "dtspsm\\__" + Trace.Name + ".fgdisplay")))
                        OneLinesToCompile.Add(Trace);
            }
            else if (Command.Equals("SCADA", StringComparison.CurrentCultureIgnoreCase))
            {
                foreach (CIM_Element SubElem in CurModel.ElementsOfType("cim:Substation"))
                    if (!File.Exists(Path.Combine(DDLDirectory, "SCADA-" + SubElem.Name + "_Dset.ddl")))
                        ElementsToProcess.Add(SubElem);
                    else if (!File.Exists(Path.Combine(ExportDirectory, "scada\\" + SubElem.Name + ".fgdisplay")))
                        OneLinesToCompile.Add(SubElem);
                foreach (CIM_Trace Trace in CurModel.Traces.Values)
                    if (!File.Exists(Path.Combine(DDLDirectory, "SCADA-" + Trace.Name + "_Dset.ddl")))
                        ElementsToProcess.Add(Trace);
                    else if (!File.Exists(Path.Combine(ExportDirectory, "scada\\__" + Trace.Name + ".fgdisplay")))
                        OneLinesToCompile.Add(Trace);
            }
            else if (Command.Equals("OTS", StringComparison.CurrentCultureIgnoreCase))
            {
                foreach (CIM_Element SubElem in CurModel.ElementsOfType("cim:Substation"))
                    if (!File.Exists(Path.Combine(DDLDirectory, "OTS-" + SubElem.Name + "_Dset.ddl")))
                        ElementsToProcess.Add(SubElem);
                    else if (!File.Exists(Path.Combine(ExportDirectory, "dtspsm\\" + SubElem.Name + ".fgdisplay")))
                        OneLinesToCompile.Add(SubElem);
                foreach (CIM_Trace Trace in CurModel.Traces.Values)
                    if (!File.Exists(Path.Combine(DDLDirectory, "OTS-" + Trace.Name + "_Dset.ddl")))
                        ElementsToProcess.Add(Trace);
                    else if (!File.Exists(Path.Combine(ExportDirectory, "dtspsm\\__" + Trace.Name + ".fgdisplay")))
                        OneLinesToCompile.Add(Trace);
                foreach (CIM_Element Company in CurModel.ElementsOfType("cim:Company"))
                    if (!File.Exists(Path.Combine(DDLDirectory, "OTS-_" + Company["cim:IdentifiedObject.aliasName"] + "_DSet.ddl")))
                        ElementsToProcess.Add(Company);
                    else if (!File.Exists(Path.Combine(ExportDirectory, "dtspsm\\___" + Company["cim:IdentifiedObject.aliasName"] + ".fgdisplay")))
                        OneLinesToCompile.Add(Company);
            }
            else if (Command.Equals("Failed", StringComparison.CurrentCultureIgnoreCase))
                using (OpenFileDialog oFd = new OpenFileDialog())
                {
                    oFd.Title = "Select the last export log";
                    oFd.Filter = "Export log (export-log*.xml)|export-log*.xml";
                    if (oFd.ShowDialog() != DialogResult.OK)
                        return;
                    XmlDocument xDoc = new XmlDocument();
                    xDoc.Load(oFd.FileName);
                    foreach (XmlElement xElem in xDoc.SelectNodes("ModelExport/Failure"))
                    {
                        CIM_RdfID FoundRdfId = new CIM_RdfID(xElem.Attributes["rdfID"].Value);
                        CIM_Element FoundElem = CurModel.FromRdfID(FoundRdfId);
                        if (FoundElem == null)
                            foreach (CIM_Trace Trace in CurModel.Traces.Values)
                                if (Trace.rdfID.Equals(FoundRdfId))
                                {
                                    FoundElem = Trace;
                                    break;
                                }
                        ElementsToProcess.Add(FoundElem);
                    }
                }
            else if (Command.Equals("PUN Elements"))
            {
                ElementsToProcess.AddRange(CurModel.PUNStations);
                foreach (CIM_Trace Trace in CurModel.Traces.Values)
                {
                    foreach (CIM_RdfID rdfID in Trace.rdfIDs)
                        if (CurModel.PUNElements.ContainsKey(rdfID))
                        {
                            ElementsToProcess.Add(Trace);
                            break;
                        }
                }
            }
            else
            {
                ElementsToProcess.AddRange(CurModel.ElementsOfType("cim:Substation"));
                ElementsToProcess.AddRange(CurModel.Traces.Values.ToArray());
            }

            //Load in all one-lines that originally passed
            bool OTSOnly = Command == "OTS";
            bool SCADAOnly = Command == "SCADA";
            using (StreamWriter sW = new StreamWriter(Path.Combine(CurModel.ModelFullPath, CurModel.NameWithoutExtension + "-OneLine_ExportLog.xml"), false, Encoding.UTF8))
            {
                sW.AutoFlush = true;
                sW.WriteLine("<?xml version=\"1.0\"?>");
                sW.WriteLine("<ModelExport>");
                foreach (CIM_Element SubElem in ElementsToProcess)
                    try
                    {
                        //Try and open up our one-line in the display
                        if (SubElem is CIM_Trace || SubElem.TypeName == "cim:Company" || (bool.Parse(SubElem["etx:Substation.forNetwork"]) && bool.Parse(SubElem["etx:Substation.forScada"])))
                            using (OracleCommand oCmd = new OracleCommand("SELECT * FROM MM_DATABASE_ONELINE WHERE UPPER(RDFID)='" + SubElem.rdfID.ToString().ToUpper() + "' AND MODELS LIKE '%," + CurModel.ID.ToString() + ",%'", Db.Database as OracleConnection))
                            using (OracleDataReader oRd = oCmd.ExecuteReader())
                                if (oRd.Read())
                                {

                                    //If we find the one-line in the database, load it and write it out.
                                    if (SubElem is CIM_Trace || Command.Equals("All", StringComparison.CurrentCultureIgnoreCase) || Command.Equals("Failed", StringComparison.CurrentCultureIgnoreCase) || Command.Equals("Missing", StringComparison.CurrentCultureIgnoreCase) || Command.Equals("OTS", StringComparison.CurrentCultureIgnoreCase) || Command.Equals("SCADA", StringComparison.CurrentCultureIgnoreCase) || oRd["LastExported"] is DBNull || Command.Equals("PUN Elements", StringComparison.CurrentCultureIgnoreCase))
                                    {
                                        MM_Database_OneLine OL = new MM_Database_OneLine(oRd, Repository);                                        
                                        using (MemoryStream InBytes = new MemoryStream((byte[])oRd["IMAGE"]))
                                        using (Bitmap DrawBitmap = new Bitmap(InBytes))
                                        {
                                            
                                            Exception CompilationException;
                                            if (SCADAOnly)
                                                CompilationException = MM_DDL_Exporter.WriteOneLine(OL, CurModel, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, ImageDirectory, DrawBitmap, Repository, true, false, false, true, MMXmlDirectory, null, Repository.Db.GetLastAssignment(OL));
                                            else
                                                CompilationException = MM_DDL_Exporter.WriteOneLine(OL, CurModel, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, ImageDirectory, DrawBitmap, Repository, !OTSOnly, !OTSOnly, true, true, MMXmlDirectory, null, Repository.Db.GetLastAssignment(OL));
                                            if (CompilationException == null)
                                            {
                                                sW.WriteLine(String.Format("\t<Success Sub=\"{0}\" TEID=\"{1}\" rdfID=\"{2}\" />", SubElem.Name, SubElem.TEID, SubElem.rdfID));
                                                using (DbCommand dCmd = Db.CreateCommand("UPDATE MM_DATABASE_ONELINE SET LASTEXPORTED=:0 WHERE ID=:1", DateTime.Now, OL.ID))
                                                    dCmd.ExecuteNonQuery();
                                            }
                                            else
                                            {
                                                sW.WriteLine(String.Format("\t<Failure Sub=\"{0}\" TEID=\"{1}\" rdfID=\"{2}\">{3}</Failure>", SubElem.Name, SubElem.TEID, SubElem.rdfID, MM_System_Interface.ConvertError(CompilationException)));
                                                ProcessLog.AppendLine("Error exporting substation one-line for " + SubElem.ToString() + ": " + CompilationException.ToString());
                                            }
                                        }
                                        Application.DoEvents();
                                        sW.Flush();
                                    }
                                }
                                else if (SubElem is CIM_Trace)
                                {
                                    XmlDocument xOneLine = CIM_BreakerToBreaker.AutoGenerateOneLine(SubElem as CIM_Trace);
                                    clsTomSawyer.RunLayout(xOneLine, CurModel);
                                    MM_Database_OneLine OneLine = new MM_Database_OneLine(null, null);
                                    OneLine.Document = xOneLine;
                                    OneLine.Models = new MM_Database_Model[] { CurModel };
                                    OneLine.Names = new string[] { SubElem.Name };
                                    OneLine.ID = -1;
                                    OneLine.OneLineType = MM_Database_OneLine.OneLineTypeEnum.BreakerToBreaker;
                                    OneLine.rdfID = SubElem.rdfID;
                                    MM_Database_Assignment LastAssignment = Repository.Db.GetLastAssignment(OneLine);
                                    Bitmap OLImg = MM_Image_Exporter.OneLineImage(OneLine, CurModel, CurModel.LocateExport(OneLine),LastAssignment);

                                    Exception CompilationException;
                                    if (SCADAOnly)
                                        CompilationException = MM_DDL_Exporter.WriteOneLine(OneLine, CurModel, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, ImageDirectory, OLImg, Repository, true, false, false, true, MMXmlDirectory,null,LastAssignment);
                                    else
                                        CompilationException = MM_DDL_Exporter.WriteOneLine(OneLine, CurModel, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, ImageDirectory, OLImg, Repository, !OTSOnly, !OTSOnly, true, true, MMXmlDirectory, null, LastAssignment);

                                    if (CompilationException == null)
                                        sW.WriteLine(String.Format("\t<Success BreakerToBreaker=\"{0}\" rdfID=\"{1}\" />", SubElem.Name, SubElem.rdfID));
                                    else
                                    {
                                        sW.WriteLine(String.Format("\t<Failure BreakerToBreaker=\"{0}\" rdfID=\"{1}\">{2}</Failure>", SubElem.Name, SubElem.rdfID, MM_System_Interface.ConvertError(CompilationException)));
                                        ProcessLog.AppendLine("Error exporting breaker-to-breaker one-line for " + SubElem.ToString() + ": " + CompilationException.ToString());
                                    }

                                }
                                else if (SubElem.TypeName == "cim:Company")
                                {

                                    XmlDocument xOneLine = CIM_BreakerToBreaker.AutoGenerateOneLine(SubElem);
                                    clsTomSawyer.RunLayout(xOneLine, Repository);
                                    MM_Database_OneLine OneLine = new MM_Database_OneLine(null, null);
                                    OneLine.Document = xOneLine;
                                    OneLine.Models = new MM_Database_Model[] { CurModel };
                                    OneLine.Names = new string[] { SubElem["cim:IdentifiedObject.aliasName"] };
                                    OneLine.ID = -1;
                                    OneLine.OneLineType = MM_Database_OneLine.OneLineTypeEnum.Operatorship;
                                    OneLine.rdfID = SubElem.rdfID;
                                    Bitmap OLImg = MM_Image_Exporter.OneLineImage(OneLine, CurModel, CurModel.LocateExport(OneLine),null);
                                    Exception CompilationException = MM_DDL_Exporter.WriteOneLine(OneLine, CurModel, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, ImageDirectory, OLImg, Repository, !OTSOnly, !OTSOnly, true, true, MMXmlDirectory, null,Repository.Db.GetLastAssignment(OneLine));
                                    if (CompilationException == null)
                                        sW.WriteLine(String.Format("\t<Success Operatorship=\"{0}\" rdfID=\"{1}\" />", SubElem.Name, SubElem.rdfID));
                                    else
                                    {
                                        sW.WriteLine(String.Format("\t<Failure Operatorship=\"{0}\" rdfID=\"{1}\">{2}</Failure>", SubElem.Name, SubElem.rdfID, MM_System_Interface.ConvertError(CompilationException)));
                                        ProcessLog.AppendLine("Error exporting company one-line for " + SubElem.ToString() + ": " + CompilationException.ToString());                                        
                                    }
                                }
                                else
                                {
                                    sW.WriteLine(String.Format("\t<Missing Sub=\"{0}\" TEID=\"{1}\" rdfID=\"{2}\" />", SubElem.Name, SubElem.TEID, SubElem.rdfID.ToString()));
                                    ProcessLog.AppendLine("Missing valid one-line for substation " + SubElem.ToString());
                                }
                    }
                    catch (Exception ex)
                    {
                        sW.WriteLine(String.Format("\t<Error {0}=\"{1}\" rdfID=\"{2}\">{3}</Error>", SubElem.TypeName.Split(':')[1], SubElem.Name, SubElem.rdfID, MM_System_Interface.ConvertError(ex)));
                        ProcessLog.AppendLine("Error exporting " + SubElem.ToString() + ": " + ex.ToString());
                    }

                //Now compile only needed one-lines
                Exception CompliationException = null;
                foreach (CIM_Element Elem in OneLinesToCompile)
                    if ((CompliationException = MM_DDL_Exporter.CompileOneLineWithConfirmation(DDLDirectory, (Elem is CIM_Trace ? "__" : Elem.TypeName == "cim:Company" ? "___" : "") + Elem.Name, ExportDirectory, ImageDirectory, EMSDirectory)) != null)
                        sW.WriteLine(String.Format("\t<Compiled {0}=\"{1}\" rdfID=\"{2}\" />", Elem is CIM_Trace ? "BreakerToBreaker" : "Substation", Elem.Name, Elem.rdfID));
                    else
                        sW.WriteLine(String.Format("\t<Compilation_Failure {0}=\"{1}\" rdfID=\"{2}\">{3}</Compilation_Failure>", Elem is CIM_Trace ? "BreakerToBreaker" : "Substation", Elem.Name, Elem.rdfID, CompliationException));


                sW.WriteLine("</ModelExport>");
            }

            WriteOutArchive(DDLDirectory, Path.Combine(TargetDirectory, CurModel.Name + "-DDLs.zip"));
            WriteOutArchive(ExportDirectory, Path.Combine(TargetDirectory, CurModel.Name + "-OneLines.zip"));
            WriteOutArchive(OLImageDirectory, Path.Combine(TargetDirectory, CurModel.Name + "-Images.zip"));
            WriteOutArchive(MMXmlDirectory, Path.Combine(TargetDirectory, CurModel.Name + "-MacomberMapOneLines.zip"));

            //Send out our confirmation email 
            ProcessLog.AppendLine();
            ProcessLog.AppendLine(String.Format("The one-line validation model was ran against CIM file {0}, and the results have been saved into {1}, along with additional diagnostics.", CurModel.Name, ExportDirectory));

            //Produce additional text about the information within.
            ProcessLog.AppendLine();
            ProcessLog.AppendLine();
            ProcessLog.AppendLine("File counts:");

            CurModel.IdealNum = int.MaxValue;
            foreach (DirectoryInfo dr in new DirectoryInfo(ExportDirectory).GetDirectories())
            {
                int FileCount = dr.GetFiles("*.fgdisplay").Length;
                ProcessLog.AppendLine(dr.Name + " (*.fgdisplay): " + FileCount.ToString("#,##0"));
                CurModel.IdealNum = Math.Min(CurModel.IdealNum, FileCount);
            }


            ProcessLog.AppendLine("DDLs (*_dset.ddl): " + new DirectoryInfo(DDLDirectory).GetFiles("*_dset.ddl").Length.ToString("#,##0"));
            ProcessLog.AppendLine("Images: " + new DirectoryInfo(OLImageDirectory).GetFiles().Length.ToString("#,##0"));
            ProcessLog.AppendLine("Macomber Map One-Lines: " + new DirectoryInfo(MMXmlDirectory).GetFiles().Length.ToString("#,##0"));
            ProcessLog.AppendLine();
            ProcessLog.AppendLine("Element counts:");
            CIM_Element[] Subs = CurModel.ElementsOfType("cim:Substation");
            CIM_Element[] Companies = CurModel.ElementsOfType("cim:Company");
            int ExportableSubs = 0;
            foreach (CIM_Element SubElem in Subs)
                if (bool.Parse(SubElem["etx:Substation.forNetwork"]) && bool.Parse(SubElem["etx:Substation.forScada"]))
                    ExportableSubs++;
            ProcessLog.AppendLine("Substations: " + Subs.Length.ToString("#,##0") + " (" + ExportableSubs.ToString("#,##0") + " exportable)");
            ProcessLog.AppendLine("Breaker-to-breaker traces: " + CurModel.Traces.Count.ToString("#,##0"));
            ProcessLog.AppendLine("Companies: " + Companies.Length.ToString("#,##0"));
            ProcessLog.AppendLine();

            ProcessLog.AppendLine("Total for substations + breaker-to-breakers: " + (ExportableSubs + CurModel.Traces.Count).ToString("#,##0") + " exports, " + (3 * (ExportableSubs + CurModel.Traces.Count)).ToString("#,##0") + " DDLs.");
            CurModel.IdealNum = ExportableSubs + CurModel.Traces.Count;
            ProcessLog.AppendLine();
            ProcessLog.AppendLine("Total for substations + breaker-to-breakers + Companies: " + (ExportableSubs + CurModel.Traces.Count + Companies.Length).ToString("#,##0") + " exports, " + (1 + 3 * (ExportableSubs + CurModel.Traces.Count + Companies.Length)).ToString("#,##0") + " DDLs");
            ProcessLog.AppendLine("Note: These compiled one-lines may contain up to " + SpecialOneLines.Length.ToString() + " special one-lines, including the following routines:");
            foreach (MM_Database_OneLine SpecialOneLine in SpecialOneLines)
                ProcessLog.AppendLine("\t" + SpecialOneLine.SpecialOneLineRoutine.Method.Name);
            if (SendEmail)
                MM_Email_Sending.SendEmail(SMTPServer, Environment.UserName + "@ercot.com", Environment.UserName, "Macomber Map Loader: One-Line Process Complete for " + CurModel.Name, MailRecipients, null, null, ProcessLog.ToString(), Path.Combine(CurModel.ModelFullPath, CurModel.NameWithoutExtension + "-OneLine_ExportLog.xml"));

            //Update our counts of compiled one-lines
            CurModel.ValidationEnd = DateTime.Now;
            CurModel.UpdateRdb(Repository);
        }


        /// <summary>
        /// Write out a ZIP archive
        /// </summary>
        /// <param name="SourceDirectory"></param>
        /// <param name="TargetZip"></param>
        public static void WriteOutArchive(String SourceDirectory, String TargetZip)
        {
            //Refresh the zip if present
            if (File.Exists(TargetZip))
                File.Delete(TargetZip);
            ZipFile OutFile = new ZipFile(TargetZip);
            OutFile.UseZip64WhenSaving = Zip64Option.AsNecessary;
            OutFile.AddDirectory(SourceDirectory);
            OutFile.Save();
        }

        /// <summary>
        /// Compile all one-lines in a thread
        /// </summary>
        /// <param name="state"></param>
        private void  OneLineCompilationThread(object state)
        {
            MM_Database_OneLine InLine;
            String ExportDirectory = ProcessString(TargetFolder);
            String ImageSource = ProcessString(FGImageDirectory);
            CIM_OneLine_Tracker Track = (CIM_OneLine_Tracker)state;
            while (true)
            {
                if (OneLinesToCompile.TryDequeue(out InLine))
                    if (InLine.OneLineType == MM_Database_OneLine.OneLineTypeEnum.Special)
                        foreach (String OneLineToCompile in InLine.SpecialOneLineRoutine(Track.Model, ProcessString(DDLDirectory)))                        
                            MM_DDL_Exporter.CompileOneLine(OneLineToCompile, ExportDirectory, ImageSource, EMSDirectory);                        
                    else
                    {

                        String SubName;
                        if (InLine.OneLineType == MM_Database_OneLine.OneLineTypeEnum.Substation)
                            SubName = Track.Model.FromRdfID(InLine.rdfID).Name;
                        else if (InLine.OneLineType == MM_Database_OneLine.OneLineTypeEnum.BreakerToBreaker)
                            SubName = "__" + InLine.Names[0];
                        else if (InLine.OneLineType == MM_Database_OneLine.OneLineTypeEnum.Operatorship)
                            SubName = "__" + InLine.Names[0];
                        else
                            SubName = InLine.Names[0];

                        MM_DDL_Exporter.CompileOneLine(Path.Combine(ProcessString(DDLDirectory), "SCADA-" + SubName + "_dset.ddl"), ExportDirectory, ImageSource, EMSDirectory);
                        MM_DDL_Exporter.CompileOneLine(Path.Combine(ProcessString(DDLDirectory), "NETMOM-" + SubName + "_dset.ddl"), ExportDirectory, ImageSource, EMSDirectory);
                        MM_DDL_Exporter.CompileOneLine(Path.Combine(ProcessString(DDLDirectory), "OTS-" + SubName + "_dset.ddl"), ExportDirectory, ImageSource, EMSDirectory);
                        if (InLine.OneLineType == MM_Database_OneLine.OneLineTypeEnum.BreakerToBreaker)
                            SubName = "__" + SubName;
                        else if (InLine.OneLineType == MM_Database_OneLine.OneLineTypeEnum.Operatorship)
                            SubName = "___" + SubName;

                        //Make sure our writer has not been disposed
                        if (Track.sW.BaseStream != null)
                        {
                            StringBuilder OutSb = new StringBuilder();
                            if (OutSb.ToString().Contains(" E:"))
                                Track.sW.WriteLine("\t<ErrorCompiling rdfID=\"{0}\" TEID=\"{1}\" SubName=\"{2}\">{3}\t</ErrorCompiling>", InLine.rdfID, InLine.TEID, InLine.Names[0], OutSb.ToString());
                            else if (!File.Exists(ExportDirectory + "\\RTNet\\" + SubName + ".fgdisplay"))
                                Track.sW.WriteLine("\t<ErrorCompiling rdfID=\"{0}\" TEID=\"{1}\" SubName=\"{2}\">{3}\t</ErrorCompiling>", InLine.rdfID, InLine.TEID, InLine.Names[0], "Unable to locate RTNET One-Line");
                            else if (!File.Exists(ExportDirectory + "\\scada\\" + SubName + ".fgdisplay"))
                                Track.sW.WriteLine("\t<ErrorCompiling rdfID=\"{0}\" TEID=\"{1}\" SubName=\"{2}\">{3}\t</ErrorCompiling>", InLine.rdfID, InLine.TEID, InLine.Names[0], "Unable to locate SCADA One-Line");
                            else if (!File.Exists(ExportDirectory + "\\DTSPSM\\" + SubName + ".fgdisplay"))
                                Track.sW.WriteLine("\t<ErrorCompiling rdfID=\"{0}\" TEID=\"{1}\" SubName=\"{2}\">{3}\t</ErrorCompiling>", InLine.rdfID, InLine.TEID, InLine.Names[0], "Unable to locate DTS One-Line");
                            else
                                Console.WriteLine("Successfully compiled " + SubName);
                        }
                    }
                Thread.Sleep(100);
            }
        }

        /// <summary>
        /// Pull one-line validation in a thread
        /// </summary>
        /// <param name="state"></param>
        private void OneLineValidatorThread(object state)
        {
            //Register this thread
            ProcessingThreads.Add(Thread.CurrentThread);

            //Pull our models in
            CIM_OneLine_Tracker Track = (CIM_OneLine_Tracker)state;

            CIM_Element OneLineToHandle;
            while (SubstationsToParse.Count > 0)
            {
                if (SubstationsToParse.TryDequeue(out OneLineToHandle))
                {                    
                        try
                        {
                            Track.CurrentOneLine++;
                            Console.WriteLine("{0}: About to validate {1}", Track.CurrentOneLine, OneLineToHandle.Name);
                            CIM_OneLineValidator.ValidateOneLine(OneLineToHandle, Track.sW, Track.Model, Repository, Track.ValidationSummaries, Track.CurrentOneLine, ProcessString(DDLDirectory), ProcessString(TargetFolder), ProcessString(OLImageDirectory), ProcessString(EMSDirectory), ProcessString(FGImageDirectory), ProcessString(MMXmlDirectory), OneLinesToCompile);
                            Track.sW.Flush();
                            Track.Successes++;
                        }
                        catch (Exception ex)
                        {
                            if (Track.sW.BaseStream != null)
                                Track.sW.WriteLine("\t<ErrorProcessing rdfID=\"{0}\" TEID=\"{1}\" SubName=\"{2}\">{3}\t</ErrorProcessing>", OneLineToHandle.rdfID, OneLineToHandle.TEID, OneLineToHandle.Name, MM_System_Interface.ConvertError(ex));
                            CIM_OneLineValidator.ApplyValidation(Track.ValidationSummaries, "Error processing", MM_Type_Converter.TitleCase(OneLineToHandle.Name));
                            Track.Failures++;
                            Console.WriteLine("Error processing " + OneLineToHandle.Name + ": " + MM_System_Interface.ConvertError(ex));
                        }
                    Current++;
                }
            }
            if (!SentSubstationValidation)
            {
                SentSubstationValidation = true;
                SendValidations(Track);
            }
        }

        /// <summary>
        /// Export our breaker-to-breaker one-lines in their own thread
        /// </summary>
        /// <param name="state"></param>
        private void TraceExportThread(Object state)
        {
            //Register this thread
            TraceProcessorThread = Thread.CurrentThread;

            //Pull our models in
            CIM_OneLine_Tracker Track = (CIM_OneLine_Tracker)state;
            CIM_Trace TraceToHandle;

            while (TracesToParse.Count > 0)
            {
                if (TracesToParse.TryDequeue(out TraceToHandle))
                {
                    try
                    {
                        XmlDocument xOneLine = CIM_BreakerToBreaker.AutoGenerateOneLine(TraceToHandle);

                        if (Track.Model.Db == null)
                            Track.Model.Db = Repository.Db;

                        clsTomSawyer.RunLayout(xOneLine, Track.Model);
                        MM_Database_OneLine OneLine = new MM_Database_OneLine(null, null);
                        OneLine.Document = xOneLine;
                        OneLine.Models = new MM_Database_Model[] { Track.Model };
                        OneLine.Names = new string[] { TraceToHandle.Name };
                        OneLine.ID = -1;
                        OneLine.OneLineType = MM_Database_OneLine.OneLineTypeEnum.BreakerToBreaker;
                        OneLine.rdfID = TraceToHandle.rdfID;
                                                
                        Exception CompilationException;
                        using (Bitmap OLImg = MM_Image_Exporter.OneLineImage(OneLine, Track.Model, Track.Model.LocateExport(OneLine),null))
                            CompilationException = MM_DDL_Exporter.WriteOneLine(OneLine, Track.Model, ProcessString(DDLDirectory), ProcessString(TargetFolder), ProcessString(OLImageDirectory), ProcessString(EMSDirectory), TraceToHandle.Name, ProcessString(FGImageDirectory), OLImg, Repository, true, true, true, true, ProcessString(MMXmlDirectory), OneLinesToCompile, Repository.Db.GetLastAssignment(OneLine));
                        if (CompilationException == null)
                        {
                            if (Track.sW.BaseStream != null)
                                Track.sW.WriteLine(String.Format("\t<Success BreakerToBreaker=\"{0}\" rdfID=\"{1}\" />", TraceToHandle.Name, TraceToHandle.rdfID, Track.Successes++));
                            Console.WriteLine("Sucessfully wrote out one-line for " + OneLine.Names[0]);
                        }
                        else
                        {
                            if (Track.sW.BaseStream != null)
                                Track.sW.WriteLine(String.Format("\t<Failure BreakerToBreaker=\"{0}\" rdfID=\"{1}\">{2}</Failure>", TraceToHandle.Name, TraceToHandle.rdfID, Track.Failures++, CompilationException.ToString()));
                            this.ProcessLog.AppendLine("Unable to compile breaker-to-breaker " + TraceToHandle.Name + ": " + CompilationException.ToString());
                        }
                    }
                    catch (Exception ex)
                    {
                        this.ProcessLog.AppendLine("Error exporting breaker-to-breaker " + TraceToHandle.Name + ": " + ex.ToString());
                        if (ex is com.tomsawyer.licensing.TSLicenseRuntimeException || ex is com.tomsawyer.service.TSServiceException)
                        {
                            Exception ex2 = ex;
                            int CurTab = 1;
                            while (ex2 != null)
                            {
                                this.ProcessLog.AppendLine(new string('\t', CurTab) + new string('-', 20));
                                this.ProcessLog.AppendLine(new String('\t', CurTab) + ex2.Message);
                                String InLine;
                                using (StringReader sRd = new StringReader(ex2.StackTrace))
                                    while ((InLine = sRd.ReadLine()) != null)
                                        this.ProcessLog.AppendLine(new String('\t', CurTab) + InLine);
                                CurTab++;
                                ex2 = ex2.InnerException;
                            }
                        }

                        if (Track.sW.BaseStream != null)
                            Track.sW.WriteLine(String.Format("\t<Error BreakerToBreaker=\"{0}\" rdfID=\"{1}\">{2}</Error>", TraceToHandle.Name, TraceToHandle.rdfID, ex.ToString(), Track.Failures++));
                    }
                }
                Current++;
            }
            Thread.Sleep(100);
        }
        
        /// <summary>
        /// Send out our validation 
        /// </summary>
        /// <param name="Track"></param>
        private void SendValidations(CIM_OneLine_Tracker Track)
        {
            StringBuilder sB3 = new StringBuilder();
            foreach (KeyValuePair<string, List<string>> kvp in Track.ValidationSummaries)
            {
                sB3.AppendLine(kvp.Key);
                foreach (String str in kvp.Value)
                    sB3.AppendLine("\t" + str);

            }

            sB3.AppendLine();
            sB3.AppendLine(ProcessLog.ToString());

            MM_Email_Sending.SendEmail(SMTPServer, Environment.UserName + "@ercot.com", Environment.UserName, "NMMS One-Line Editor: One-line validation for " + Track.Model.FullClass + " completed, b2b exporting commencing.", MailRecipients, null, null, ProcessString(sB3.ToString()));
              //Go through each user and identify how many one-lines are assigned to them
            foreach (MM_Database_User User in Repository.User_Collection.Values)
            {
                StringBuilder sB = new StringBuilder();
                StringBuilder sB2 = new StringBuilder();
                using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT Errors, Warnings, CompletedOn, OneLineName, Notes FROM MM_DATABASE_ASSIGNMENT WHERE ASSIGNEDTO=:0 AND Model=:1", User.ID, Track.Model.ID))
                using (DbDataReader dRd = dCmd.ExecuteReader())
                    while (dRd.Read())
                    {
                        if (dRd["CompletedOn"] is DBNull)
                            sB.AppendLine(String.Format("{0} ({1} Errors, {2} Warnings)", dRd["OneLineName"], dRd["Errors"], dRd["Warnings"]));
                        else
                            sB.AppendLine(dRd["OneLineName"].ToString() + " (already repaired)");
                        sB2.AppendLine(dRd["OneLineName"].ToString());
                        String InLine;
                        using (StringReader sRdr = new StringReader(dRd["Notes"].ToString()))
                            while ((InLine = sRdr.ReadLine()) != null)
                                sB2.AppendLine("\t" + InLine);
                        sB2.AppendLine();
                    }

                if (sB.Length > 0)
                    MM_Email_Sending.SendEmail(SMTPServer, Environment.UserName + "@ercot.com", Environment.UserName, "One-Line Assigment report for " + Track.Model.Name, new MM_String_Collection(new string[] { User.Email }), null, null, "This is the Macomber Map Loader on " + Environment.MachineName + ", running against model " + Track.Model.Name + ". The following one-lines show topology/layout issues and require intervention:\r\n" + sB.ToString() + "\r\n\r\n" + sB2.ToString());
            }
        }

        /// <summary>
        /// Import our collection of validation summaries
        /// </summary>
        /// <param name="Repository"></param>
        /// <param name="Model"></param>
        /// <returns></returns>
        public static Dictionary<String, List<String>> ImportValidationSummaries(MM_Repository Repository, MM_Database_Model Model)
        {
            Dictionary<string, List<string>> ValidationSummaries = new Dictionary<string, List<string>>();

            //Import old one-assignments and prior assignments
            using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT ONELINENAME, NOTES FROM MM_DATABASE_ASSIGNMENT WHERE MODEL=:0", Model.ID))
            using (DbDataReader dRd = dCmd.ExecuteReader())
                while (dRd.Read())
                    ApplyValidation(ValidationSummaries, dRd["NOTES"].ToString(), dRd["ONELINENAME"].ToString());
            using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT RDFID,MODELS FROM MM_DATABASE_ONELINE WHERE MODELS LIKE :0", "%" + Model.ID + "%"))
            using (DbDataReader dRd = dCmd.ExecuteReader())
                while (dRd.Read())
                {
                    String[] Models = dRd["MODELS"].ToString().Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    if (!Models.Contains(Model.PriorModel.ID.ToString()))
                    {
                        MM_Database_Model LastModel ;
                        int IndexOfModel = Array.IndexOf(Models, Model.ID.ToString());
                        if (IndexOfModel == -1)
                            LastModel = Repository.Model_Collection[int.Parse(Models[Models.Length - 1])];
                        else if (IndexOfModel == 0)
                            LastModel = null;
                        else 
                            LastModel = Repository.Model_Collection[int.Parse(Models[IndexOfModel- 1])];
                                                
                        CIM_Element SubElem = Model.FromRdfID(dRd["RDFID"].ToString());                        
                        if (LastModel != null)
                            ApplyValidation(ValidationSummaries, "Imported from prior models", MM_Type_Converter.TitleCase(SubElem.Name) + " (Imported from " + LastModel.ModelCategory + " " + LastModel.FullClass + ")");
                    }
                }
            return ValidationSummaries;
        }

        /// <summary>
        /// Initiate our export
        /// </summary>
        /// <param name="DbConn"></param>
        public override enumProcessState InitiateProcess(MM_Database_Connector DbConn)
        {
            MM_Database_Model Model = NamedParameters["Model"] as MM_Database_Model;

            //Make sure our model exists. If not, we'll have to add it in.
            if (Model.ID == 0)
                using (DbCommand dCmd = Repository.Db.CreateCommand("SELECT ID FROM MM_DATABASE_MODEL WHERE FULLPATH=:0", Model.FullPath))
                {
                    object inObj = dCmd.ExecuteScalar();
                    if (inObj == null || inObj is DBNull)
                        Model.WriteRdb(Repository);
                    else
                        Model.ID = Convert.ToInt32(inObj);
                }

            //Ensure our directories exist
            if (!Directory.Exists(ProcessString(TargetFolder)))
                Directory.CreateDirectory(ProcessString(TargetFolder));
            if (!Directory.Exists(ProcessString(DDLDirectory)))
                Directory.CreateDirectory(ProcessString(DDLDirectory));
            if (!String.IsNullOrEmpty(MMXmlDirectory) && !Directory.Exists(ProcessString(MMXmlDirectory)))
                Directory.CreateDirectory(ProcessString(MMXmlDirectory));

            //Ensure our ESet exists
            if (!File.Exists(Path.Combine(ProcessString(DDLDirectory), "ercotonelines_eset.ddl")))
                using (StreamWriter sESET = new StreamWriter(Path.Combine(ProcessString(DDLDirectory), "ercotonelines_eset.ddl"), false, Encoding.ASCII))
                    sESET.Write(Data_Manager.WriteColorsAndModelSpecifics(Resources.ERCOTOneLines_ESet, Model));


            //Tally up all the one-lines we need to process
            foreach (CIM_Element SubElem in Model.ElementsOfType("cim:Substation"))
                if (bool.Parse(SubElem["etx:Substation.forNetwork"]) && bool.Parse(SubElem["etx:Substation.forScada"]))
                    SubstationsToParse.Enqueue(SubElem);
            foreach (CIM_Trace Trace in Model.Traces.Values)
                TracesToParse.Enqueue(Trace);


            //Update our model information
            this.Current = 0;
            this.Total = Model.IdealNum = SubstationsToParse.Count;
            Model.ValidationStart = DateTime.Now;
            Model.ValidationBy = Repository.CurrentUser;
            Model.UpdateRdb(Repository);

            //Start our XML file
            String ValidationLogName = Path.Combine(Model.ModelFullPath, Model.NameWithoutExtension + "-OneLineValidationLog.xml");
            CIM_OneLine_Tracker Tracker = new CIM_OneLine_Tracker(Model, new StreamWriter(ValidationLogName, true, Encoding.UTF8), ImportValidationSummaries(Repository, Model));

            bool NewFile = !File.Exists(ValidationLogName);
            if (NewFile)
            {
                Tracker.sW.WriteLine("<?xml version=\"1.0\"?>");
                Tracker.sW.WriteLine(String.Format("<OneLineProcessing Model=\"{0}\" LastModel=\"{1}\" RanOn=\"{2}\">", Model.Name, Model.PriorModel.Name, XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified)));
            }
            else
                Tracker.sW.WriteLine("<!-- RESUMED ON {0} -->", DateTime.Now.ToString());

            //Add all special routines
            foreach (MM_Database_OneLine SpecialOneLine in SpecialOneLines)
                OneLinesToCompile.Enqueue(SpecialOneLine);

            //Now, kick off all of our threads appropriate            
            for (int a = 0; a < Math.Min(Environment.ProcessorCount, 4) - 1; a++)
                ThreadPool.QueueUserWorkItem(new WaitCallback(OneLineValidatorThread), Tracker);
            ThreadPool.QueueUserWorkItem(new WaitCallback(TraceExportThread), Tracker);
            ThreadPool.QueueUserWorkItem(new WaitCallback(OneLineCompilationThread), Tracker);

            //Kick off our thread for handling 

            //Now, wait for everything to finish up.
            while (SubstationsToParse.Count > 0 || OneLinesToCompile.Count > 0)
            {
                Application.DoEvents();
                Thread.Sleep(500);
            }

            Tracker.sW.WriteLine("</OneLineProcessing>");
            Tracker.sW.Close();
            Tracker.sW.Dispose();

            //Write out our validation summary
            String ValidationSummary = Path.Combine(Model.ModelFullPath, Model.NameWithoutExtension + "-OneLine_ValidationSummary.txt");
            if (!File.Exists(ValidationSummary))
            {
                ProcessLog.AppendLine(String.Format("This is the Macomber Map Loader 3's One-line validation Module, version {0}, running on {1}, by {2}\\{3} at {4}", Application.ProductVersion, Environment.MachineName, Environment.UserDomainName, Environment.UserName, DateTime.Now));
                ProcessLog.AppendLine();
                ProcessLog.AppendLine(String.Format("The one-line validation model was ran against CIM file {0}, and the results have been saved into {1}, along with additional diagnostics.", ProcessString(Model.Name), ProcessString(TargetFolder)));
            }
            else
                ProcessLog.AppendLine(" ----- RESUMED ON " + DateTime.Now.ToString() + " -----");
            ProcessLog.AppendLine();
            ProcessLog.AppendLine();
            using (StreamWriter sW2 = new StreamWriter(ValidationSummary, true, Encoding.UTF8))
                foreach (KeyValuePair<String, List<string>> kvp in Tracker.ValidationSummaries)
                {
                    kvp.Value.Sort();
                    sW2.WriteLine(kvp.Key + ":");
                    ProcessLog.AppendLine(kvp.Key + ":");
                    foreach (String str in kvp.Value)
                    {
                        sW2.WriteLine(str);
                        ProcessLog.AppendLine("\t" + str);
                    }
                    ProcessLog.AppendLine();
                    sW2.WriteLine();
                }


            //Send out the validation email
            //String ValidationLogName = Path.Combine(Model.ModelFullPath, Model.NameWithoutExtension + "-OneLineValidationLog.xml");
            MM_Email_Sending.SendEmail(SMTPServer, Environment.UserName + "@ercot.com", Environment.UserName, ProcessString(Subject), MailRecipients, null, null, ProcessString(ProcessLog.ToString()), ValidationLogName);


            //Now, wait until the remaining one-lines are completed
            bool ReCheck = true;
            while (ReCheck)
            {
                Thread.Sleep(10000);
                using (DbCommand dCmd = DbConn.CreateCommand("SELECT COUNT(*) FROM MM_DATABASE_ASSIGNMENT WHERE MODEL=:0 AND COMPLETEDON IS NULL", Model.ID))
                    ReCheck = Convert.ToInt32(dCmd.ExecuteScalar()) != 0;
            }

            //Now, send out an email that final export has commenced.
            MM_Email_Sending.SendEmail(SMTPServer, Environment.UserName + "@ercot.com", Environment.UserName, "Macomber Map Loader: One-Line Assignments Complete.", MailRecipients, null, null, "All one-lines have completed validation or have been updated for " + Model.Name + ", and the export and archiving process has began.");
            ExportOneLine("Missing", Model, Model.FullPath, ProcessString(DDLDirectory), ProcessString(TargetFolder), ProcessString(OLImageDirectory), ProcessString(EMSDirectory), ProcessString(FGImageDirectory), DbConn, Repository, true, SMTPServer, ProcessString(Subject), MailRecipients, ProcessString(MMXmlDirectory), ProcessLog, false);


            return enumProcessState.Completed;
        }

        /// <summary>
        /// This class holds information on a one-line tracker
        /// </summary>
        public class CIM_OneLine_Tracker
        {
            /// <summary>The current one-line being shown</summary>
            public int CurrentOneLine = 0;

            /// <summary>The model associated with the one-line</summary>
            public MM_Database_Model Model;

            /// <summary>The stream writer associated with our files writing out</summary>
            public StreamWriter sW;

            /// <summary>The collection of validation summaries</summary>
            public Dictionary<String, List<String>> ValidationSummaries;

            /// <summary>Our success count</summary>
            public int Successes = 0;

            /// <summary>Our failure count</summary>
            public int Failures = 0;

            /// <summary>
            /// Initialize a new one-line tracker
            /// </summary>
            /// <param name="Model"></param>
            /// <param name="sW"></param>
            /// <param name="ValidationSummaries"></param>
            public CIM_OneLine_Tracker(MM_Database_Model Model, StreamWriter sW, Dictionary<String, List<String>> ValidationSummaries)
            {
                this.Model = Model;
                this.sW = sW;
                this.ValidationSummaries = ValidationSummaries;
            }
        }

        /// <summary>
        /// Perform the full validation of a single one-line
        /// </summary>
        /// <param name="SubElem"></param>
        /// <param name="sW"></param>
        /// <param name="Model"></param>
        /// <param name="Repository"></param>
        /// <param name="ValidationSummaries"></param>
        /// <param name="CurrentOneLine"></param>
        /// <param name="EMSDirectory"></param>
        /// <param name="DDLDirectory"></param>
        /// <param name="ExportDirectory"></param>
        /// <param name="OLImageDirectory"></param>
        /// <param name="FGImageDirectory"></param>
        /// <param name="MMXmlDirectory"></param>
        /// <param name="OneLinesToCompile"></param>
        public static void ValidateOneLine(CIM_Element SubElem, StreamWriter sW, MM_Database_Model Model, MM_Repository Repository, Dictionary<String, List<String>> ValidationSummaries, int CurrentOneLine, String DDLDirectory, String ExportDirectory, String OLImageDirectory, String EMSDirectory, String FGImageDirectory, String MMXmlDirectory, ConcurrentQueue<MM_Database_OneLine> OneLinesToCompile)
        {
            List<MM_Database_OneLine> SubstationOneLines = new List<MM_Database_OneLine>();
            bool AlreadyValidated = false;
            int LastModelErr = int.MaxValue, LastModelWrn = int.MaxValue;
            String LastModelValidation = "";
            MM_Database_Connector Db = Repository.Db;
            using (DbCommand dCmd2 = Db.CreateCommand("SELECT COUNT(ID) FROM MM_DATABASE_ONELINE WHERE ONELINETYPE=:0 AND MODELS LIKE :1 AND UPPER(RDFID)=:2", 0, "%," + Model.ID + ",%", SubElem.rdfID.ToString().ToUpper()))
                if (Model.ID > 0 && Convert.ToInt32(dCmd2.ExecuteScalar()) > 0)
                {
                    sW.WriteLine("\t<AlreadyRepaired Substation=\"{0}\" TEID=\"{1}\" rdfID=\"{2}\"/>", SubElem.Name, SubElem.TEID, SubElem.rdfID);
                    Console.WriteLine("Already repaired substation " + SubElem.Name + " (" + SubElem.TEID + "/" + SubElem.rdfID + ")");
                    AlreadyValidated = true;
                    CIM_OneLineValidator.ApplyValidation(ValidationSummaries, "Already repaired", MM_Type_Converter.TitleCase(SubElem.Name));                    
                }
                else
                {
                    using (DbCommand dCmd = Db.CreateCommand("SELECT * FROM MM_DATABASE_ONELINE WHERE ONELINETYPE=:0 AND UPPER(RDFID)=:1", 0, SubElem.rdfID.ToString().ToUpper()))
                    using (DbDataReader dRd = dCmd.ExecuteReader())
                        while (dRd.Read())
                            SubstationOneLines.Add(new MM_Database_OneLine(dRd, Repository));

                    //Determine our highest revision number
                    int MaxLayout = 0;
                    foreach (MM_Database_OneLine OL in SubstationOneLines)
                        MaxLayout = Math.Max(MaxLayout, OL.LayoutVersion);

                    //First, look for the current model
                    MM_Database_OneLine BestOL = null;
                    MM_Oneline_Export BestExp = null;
                    int BestErr = 0, BestWrn = 0;
                    String BestValidation = "";
                    Image BestImage = null;
                    foreach (MM_Database_OneLine OL in SubstationOneLines)
                        if (Array.IndexOf(OL.Models, Model) != -1)
                        {
                            sW.WriteLine("\t<AlreadyValidated Substation=\"{0}\" TEID=\"{1}\" rdfID=\"{2}\"/>", SubElem.Name, SubElem.TEID, SubElem.rdfID);
                            Console.WriteLine("Already validated substation " + SubElem.Name + " (" + SubElem.TEID + "/" + SubElem.rdfID + "):" + CurrentOneLine.ToString("#,##0"));
                            BestOL = OL;
                            AlreadyValidated = true;
                            CIM_OneLineValidator.ApplyValidation(ValidationSummaries, "Already processed", MM_Type_Converter.TitleCase(SubElem.Name));

                            break;
                        }

                    //If we haven't already validated, go through all models (starting with the prior one)
                    if (!AlreadyValidated)
                    {
                        foreach (MM_Database_OneLine OL in SubstationOneLines)
                            if (Array.IndexOf(OL.Models, Model.PriorModel) != -1 && OL.LayoutVersion == MaxLayout)
                            {
                                CIM_OneLineValidator.ValidateOneLine(OL, Model, SubElem, ref BestOL, ref BestExp, ref BestErr, ref BestWrn, ref BestValidation, ref BestImage, Repository.Db);
                                LastModelErr = BestErr;
                                LastModelWrn = BestWrn;
                                LastModelValidation = BestValidation;
                            }

                        //If we don't have perfection, look around                                
                        foreach (MM_Database_OneLine OL in SubstationOneLines)
                            if ((BestErr > 0 || BestWrn > 0 || BestOL == null) && Array.IndexOf(OL.Models, Model.PriorModel) == -1 && OL.LayoutVersion == MaxLayout)
                                CIM_OneLineValidator.ValidateOneLine(OL, Model, SubElem, ref BestOL, ref BestExp, ref BestErr, ref BestWrn, ref BestValidation, ref BestImage, Repository.Db);

                    }

                    //Now, we have the best one-line. Let's look at it.
                    if (AlreadyValidated)
                        MM_DDL_Exporter.WriteOneLine(BestOL, Model, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, FGImageDirectory, BestImage, Repository, true, true, true, true, MMXmlDirectory, OneLinesToCompile, Repository.Db.GetLastAssignment(BestOL));

                    else if (SubstationOneLines.Count == 0)
                    {
                        sW.WriteLine(String.Format("\t<NotFound Substation=\"{0}\" TEID=\"{1}\" rdfID=\"{2}\"/>", SubElem.Name, SubElem.TEID, SubElem.rdfID));
                        Console.WriteLine("Unable to find substation " + SubElem.Name + " (" + SubElem.TEID + "/" + SubElem.rdfID + "): " + CurrentOneLine.ToString("#,##0"));
                        MM_Substation FoundSub;
                        if (Repository.Db.Substations.TryGetValue(SubElem.rdfID, out FoundSub))
                            CIM_OneLineValidator.ApplyValidation(ValidationSummaries, "New Substations", MM_Type_Converter.TitleCase(SubElem.Name) + "(Lat=" + FoundSub.Latitude.ToString() + ", Lng=" + FoundSub.Longitude.ToString() + " in county " + FoundSub.Boundary.Name + ")");
                        else
                            CIM_OneLineValidator.ApplyValidation(ValidationSummaries, "New Substations", MM_Type_Converter.TitleCase(SubElem.Name) + "(Coordinates unknown).");


                        //Build a new one-line that's blank
                        MM_Database_OneLine NewOL = new MM_Database_OneLine(null, null);
                        NewOL.Models = new MM_Database_Model[] { Model };
                        NewOL.Names = new string[] { SubElem.Name };
                        NewOL.OneLineType = MM_Database_OneLine.OneLineTypeEnum.Substation;
                        NewOL.rdfID = SubElem.rdfID;
                        NewOL.TEID = Convert.ToUInt64(SubElem.TEID);
                        NewOL.Document = new XmlDocument();
                        XmlElement RootElem = NewOL.Document.CreateElement("One_Line");
                        NewOL.Document.AppendChild(RootElem);
                        RootElem.Attributes.Append(NewOL.Document.CreateAttribute("BaseElement.ElemType")).Value = "Substation";
                        RootElem.Attributes.Append(NewOL.Document.CreateAttribute("BaseElement.TEID")).Value = SubElem.TEID;
                        RootElem.Attributes.Append(NewOL.Document.CreateAttribute("BaseElement.DisplayName")).Value = MM_Type_Converter.TitleCase(SubElem["cim:IdentifiedObject.Description"]);
                        RootElem.Attributes.Append(NewOL.Document.CreateAttribute("BaseElement.LongName")).Value = MM_Type_Converter.TitleCase(SubElem["cim:IdentifiedObject.Description"]);
                        RootElem.Attributes.Append(NewOL.Document.CreateAttribute("BaseElement.Name")).Value = MM_Type_Converter.TitleCase(SubElem.Name);
                        RootElem.Attributes.Append(NewOL.Document.CreateAttribute("ExportDate")).Value = XmlConvert.ToString(DateTime.Now, XmlDateTimeSerializationMode.Unspecified);
                        RootElem.Attributes.Append(NewOL.Document.CreateAttribute("LastCIM")).Value = Model.Name;
                        CIM_OneLineValidator.AssignOneLine(NewOL, Model, SubElem, "Substation " + SubElem.Name + " not found - new substation one-line created.", SubElem.Elements.Length, 0, Db, ValidationSummaries);
                    }
                    else if (BestErr == 0 && BestWrn == 0)
                    {
                        CheckOneLineName(BestOL, SubElem, ValidationSummaries, Repository);               
                        using (DbCommand dCmd = Db.CreateCommand("UPDATE MM_DATABASE_ONELINE SET MODELS = MODELS || :0 WHERE ID=:1", Model.ID.ToString() + ",", BestOL.ID))
                            dCmd.ExecuteNonQuery();



                        String ImportedFrom = "";
                        //If our best one-line isn't the most recent, update accordingly.
                        if (LastModelErr + LastModelWrn > 0)
                        {
                            MM_Database_Model LastModel = null;
                            for (int a = BestOL.Models.Length - 1; a >= 0; a--)
                                if (LastModel == null && BestOL.Models[a] != null)
                                    LastModel = BestOL.Models[a];
                            if (LastModel != null)
                            {
                                CIM_OneLineValidator.ApplyValidation(ValidationSummaries, LastModelValidation, MM_Type_Converter.TitleCase(SubElem.Name) + " (Imported from " + LastModel.ModelCategory + " " + LastModel.FullClass + ")");
                                ImportedFrom = "ImportedFrom=\"" + LastModel.ModelCategory + " " + LastModel.FullClass + "\"";
                            }
                            else
                            {
                                CIM_OneLineValidator.ApplyValidation(ValidationSummaries, LastModelValidation, MM_Type_Converter.TitleCase(SubElem.Name) + " (Imported from database)");
                                ImportedFrom = "ImportedFrom=\"Database\"";
                            }
                        }

                        Exception CompilationError = MM_DDL_Exporter.WriteOneLine(BestOL, Model, DDLDirectory, ExportDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, FGImageDirectory, BestImage, Repository, true, true, true, true, MMXmlDirectory, OneLinesToCompile, Repository.Db.GetLastAssignment(BestOL));
                        if (CompilationError == null)
                        {
                            sW.WriteLine(string.Format("\t<Success ID=\"{0}\" TEID=\"{1}\" SubName=\"{2}\" {3}/>", BestOL.ID, SubElem.TEID, SubElem.Name, ImportedFrom));
                            Console.WriteLine("Success on " + String.Join(",", BestOL.Names.ToArray()) + ": " + CurrentOneLine.ToString("#,##0") + " " + ImportedFrom);
                        }
                        else
                        {
                            sW.WriteLine(string.Format("\t<Failure_Export ID=\"{0}\" TEID=\"{1}\" SubName=\"{2}\" {3}>{4}</Failure_Export>", BestOL.ID, SubElem.TEID, SubElem.Name, ImportedFrom, CompilationError.ToString()));
                            Console.WriteLine("Export/compile failure on " + String.Join(",", BestOL.Names.ToArray()) + ": " + CurrentOneLine.ToString("#,##0") + " " + ImportedFrom);
                            CIM_OneLineValidator.ApplyValidation(ValidationSummaries, "Error exporting", MM_Type_Converter.TitleCase(SubElem.Name));
                        }
                    }
                    else if (BestErr == 0)
                    {
                        sW.WriteLine(string.Format("\t<Warnings Warnings=\"{0}\" ID=\"{1}\" TEID=\"{2}\" SubName=\"{3}\">{4}\t</Warnings>", BestWrn, BestOL.ID, SubElem.TEID, SubElem.Name, BestValidation));
                        //WriteOneLine(BestOL, Model, DDLDirectory, TargetDirectory, OLImageDirectory, EMSDirectory, SubElem.Name, ImageDirectory, BestImage);
                        int NewOneLineID = CIM_OneLineValidator.AssignOneLine(BestOL, Model, SubElem, BestValidation, BestErr, BestWrn, Db, ValidationSummaries);
                        //Db.CreateCommand("UPDATE MM_DATABASE_ONELINE SET MODELS = MODELS || :0 WHERE ID=:1", Model.ID.ToString() + ",", BestOL.ID).ExecuteNonQuery();
                        CIM_OneLineValidator.ApplyValidation(ValidationSummaries, BestValidation, MM_Type_Converter.TitleCase(SubElem.Name));
                        Console.WriteLine("Warning on " + String.Join(",", BestOL.Names.ToArray()) + ": " + CurrentOneLine.ToString("#,##0"));
                    }
                    else
                    {
                        int NewOneLineID = CIM_OneLineValidator.AssignOneLine(BestOL, Model, SubElem, BestValidation, BestErr, BestWrn, Db, ValidationSummaries);
                        sW.WriteLine(string.Format("\t<Errors Errors=\"{0}\" Warnings=\"{1}\" ID=\"{2}\" TEID=\"{3}\" SubName=\"{4}\">{5}\t</Errors>", BestErr, BestWrn, NewOneLineID, SubElem.TEID, SubElem.Name, BestValidation));
                        Console.WriteLine("Error on " + String.Join(",", BestOL.Names.ToArray()) + ": " + CurrentOneLine.ToString("#,##0")); //":\n" + BestValidation );
                        CIM_OneLineValidator.ApplyValidation(ValidationSummaries, BestValidation, MM_Type_Converter.TitleCase(SubElem.Name));
                    }                    
                }
            SubstationOneLines.Clear();            
            sW.Flush();
            GC.Collect();
        }


        /// <summary>
        /// Determine whether a substation one-line name is contained in the associated names, and if not, update it.
        /// </summary>
        /// <param name="BestOL"></param>
        /// <param name="BaseElement"></param>
        /// <param name="ValidationSummaries"></param>
        /// <param name="Repository"></param>
        public static void CheckOneLineName(MM_Database_OneLine BestOL, CIM_Element BaseElement, Dictionary<string, List<string>> ValidationSummaries, MM_Repository Repository)
        {
            String SubName = MM_Type_Converter.TitleCase(BaseElement.Name);
            foreach (String str in BestOL.Names)
                if (str.Equals(SubName, StringComparison.CurrentCultureIgnoreCase))
                    return;

            
            //Append our name to our list
            List<string> SubNames = new List<string>(BestOL.Names);
            SubNames.Add(SubName);
            BestOL.Names = SubNames.ToArray();
            using (DbCommand dCmd = Repository.Db.CreateCommand("UPDATE MM_DATABASE_ONELINE SET NAMES = :0 WHERE ID=:1", String.Join(",", SubNames), BestOL.ID))
                dCmd.ExecuteNonQuery();

            //Notify our user
            InsertValidation(ValidationSummaries, "Substation name changes", SubName + " (former names include " + String.Join(", ", SubNames) + ")");
        }

        /// <summary>
        /// Apply validation text
        /// </summary>
        /// <param name="ValidationSummaries"></param>
        /// <param name="ValidationText"></param>
        /// <param name="SubName"></param>
        public static void ApplyValidation(Dictionary<string, List<string>> ValidationSummaries, string ValidationText, string SubName)
        {
            if (!ValidationText.Contains('\n'))
                InsertValidation(ValidationSummaries, ValidationText, SubName);
            else
            {
                bool FoundOutput = false;
                if (ValidationText.Contains("does not have a secondary descriptor"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations needing display updates due to resource node changes", SubName);
                if (ValidationText.Contains("The normal open status of"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations with breaker/switch normal open changes", SubName);
                if (ValidationText.Contains("in the one-line, but not in topology") || ValidationText.Contains("in the topology, but not in the one-line"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations with topological changes", SubName);
                if (ValidationText.Contains("intersects with"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "One-lines needing display updates due to element collisions (e.g., add'l SCADA analogs)", SubName);
                if (ValidationText.Contains("should have its node's direction ="))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations needing pricing vector updates", SubName);
                if (ValidationText.Contains("has an unnecessary descriptor"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations needing descriptors removed", SubName);
                if (ValidationText.Contains("but allowed orientations"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations with elements needing re-orientation", SubName);
                if (ValidationText.Contains("ERROR: "))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations that could not be validated", SubName);
                if (ValidationText.Contains("Transformer/winding orientation mismatch"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations with transformers needing re-orientation", SubName);
                if (ValidationText.Contains("does not have a descriptor"))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations with elements needing descriptors", SubName);
                if (ValidationText.Contains("The one-line treats element with name "))
                    FoundOutput |= InsertValidation(ValidationSummaries, "Substations with elements changing types", SubName);

                if (!FoundOutput)
                    File.AppendAllText("ValidationUnknown.log", "Unknown validation results for " + SubName + ":\n" + ValidationText + "\n\n\n");
            }

        }

        /// <summary>
        /// Insert a validation element
        /// </summary>
        /// <param name="ValidationSummaries"></param>
        /// <param name="ValidationText"></param>
        /// <param name="SubName"></param>
        private static bool InsertValidation(Dictionary<string, List<string>> ValidationSummaries, string ValidationText, string SubName)
        {
            List<String> FoundTarget;
            lock (ValidationSummaries)            
                if (!ValidationSummaries.TryGetValue(ValidationText, out FoundTarget))
                    ValidationSummaries.Add(ValidationText, FoundTarget = new List<string>());
            lock (FoundTarget)
                FoundTarget.Add(SubName);            
            return true;
        }

        /// <summary>
        /// Clone a one-line out
        /// </summary>
        /// <param name="InLine"></param>
        /// <param name="Model"></param>
        /// <param name="BaseElement"></param>
        /// <param name="ValidationNotes"></param>
        /// <param name="Errors"></param>
        /// <param name="Warnings"></param>
        /// <param name="Db"></param>
        /// <param name="ValidationSummaries"></param>
        public static int AssignOneLine(MM_Database_OneLine InLine, MM_Database_Model Model, CIM_Element BaseElement, String ValidationNotes, int Errors, int Warnings, MM_Database_Connector Db, Dictionary<String, List<String>> ValidationSummaries)
        {
            CheckOneLineName(InLine, BaseElement, ValidationSummaries, Db.Repository);

            //First, pull in our list of available users 
            int LowestID = 0, LowestCount = int.MaxValue, LowestTotal = int.MaxValue;
            using (DbCommand oCmd = Db.CreateCommand("SELECT \"ID\" FROM MM_DATABASE_USER WHERE USERSTATE=0 AND " + (Errors > 0 ? "HANDLEERRORS" : "HANDLEWARNINGS") + "=1"))
            using (DbDataReader oRd = oCmd.ExecuteReader())
                while (oRd.Read())
                {
                    int ID = Convert.ToInt32(oRd["ID"]);
                    int UserCount = 0, UserTotal = 0;
                    using (DbCommand oCmd2 = Db.CreateCommand("SELECT ERRORS, WARNINGS FROM MM_DATABASE_ASSIGNMENT WHERE ASSIGNEDTO=:0 AND COMPLETEDON IS NULL", ID))
                    using (DbDataReader oRd2 = oCmd2.ExecuteReader())
                        while (oRd2.Read())
                        {
                            UserCount++;
                            UserTotal += Convert.ToInt32(oRd2["Errors"]) + Convert.ToInt32(oRd2["Warnings"]);
                        }

                    //10/20/2011 - Modified assignment on number of errors+warnings, not # of one-lines
                    if (UserTotal < LowestTotal)
                    //if (UserCount < LowestCount)
                    {
                        LowestID = ID;
                        LowestCount = UserCount;
                        LowestTotal = UserTotal;
                    }
                }


            if (LowestCount == int.MaxValue)
                throw new InvalidOperationException("Unable to find an available user to assign this one-line!");

            int NewID = InLine.ID;

            //If we have errors, create a new one-line
            //if (Errors > 0)
            using (OracleCommand oCmd = new OracleCommand("INSERT INTO MM_DATABASE_ONELINE (TEID, RDFID, IMAGE, DOCUMENT, MODELS, ID, NAMES, ONELINETYPE, LAYOUTVERSION) VALUES (:0, :1, :2, :3, :4, SEQ_MM_DATABASE_ONELINE.NextVal, :5, :6, :7) RETURNING ID INTO :NewID", Db.Database as OracleConnection))
            {
                oCmd.CommandType = CommandType.Text;
                oCmd.Prepare();
                oCmd.Parameters.Add(Db.CreateParameter(":0", Convert.ToUInt64(BaseElement.TEID)));
                oCmd.Parameters.Add(Db.CreateParameter(":1", BaseElement.rdfID.ToString()));
                if (InLine.ID == int.MinValue)
                    oCmd.Parameters.Add(Db.CreateParameter(":2", new Bitmap(100,100)));
                else
                    oCmd.Parameters.Add(Db.CreateParameter(":2", Db.LoadOneLineImage(InLine.ID)));
                oCmd.Parameters.Add(Db.CreateParameter(":3", InLine.Document));
                oCmd.Parameters.Add(Db.CreateParameter(":4", "," + Model.ID.ToString() + ","));
                oCmd.Parameters.Add(Db.CreateParameter(":5", MM_Type_Converter.TitleCase(BaseElement.Name)));
                oCmd.Parameters.Add(Db.CreateParameter(":6", 0));
                oCmd.Parameters.Add(Db.CreateParameter(":7", InLine.LayoutVersion));
                DbParameter  oParam = Db.CreateParameter("NewID", 0);
                oParam.Direction = ParameterDirection.ReturnValue;
                oCmd.Parameters.Add(oParam);


                int RowsInserted = oCmd.ExecuteNonQuery();
                if (RowsInserted != 1)
                    MessageBox.Show("Warning! Error inserting the rows for element " + BaseElement.ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
                else
                    NewID = Convert.ToInt32(oParam.Value);
            }

            using (OracleCommand oCmd = new OracleCommand("INSERT INTO MM_DATABASE_ASSIGNMENT(\"ONELINE\", \"MODEL\", ASSIGNEDTO, \"ASSIGNEDON\",\"NOTES\", \"ID\",\"ERRORS\",\"WARNINGS\", \"ONELINETYPE\", \"ONELINENAME\") VALUES (:6, :7, :8, :9, :10, SEQ_MM_DATABASE_ASSIGNMENT.NEXTVAL,:11,:12, :13, :14)", Db.Database as OracleConnection))
            {
                oCmd.Prepare();
                oCmd.Parameters.Add(Db.CreateParameter(":6", NewID));
                oCmd.Parameters.Add(Db.CreateParameter(":7", Model.ID));
                oCmd.Parameters.Add(Db.CreateParameter(":8", LowestID));
                oCmd.Parameters.Add(Db.CreateParameter(":9", DateTime.Now));
                oCmd.Parameters.Add(Db.CreateParameter(":10", ValidationNotes));
                oCmd.Parameters.Add(Db.CreateParameter(":11", Errors));
                oCmd.Parameters.Add(Db.CreateParameter(":12", Warnings));
                oCmd.Parameters.Add(Db.CreateParameter(":13", 0));
                oCmd.Parameters.Add(Db.CreateParameter(":14", MM_Type_Converter.TitleCase(BaseElement.Name)));
                int RowsInserted = oCmd.ExecuteNonQuery();
                if (RowsInserted != 1)
                    MessageBox.Show("Warning! Error inserting the assignment for element " + BaseElement.ToString(), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            return NewID;
        }


        /// <summary>
        /// Perform validation against a one-line. If we have a better one than our reference, update.
        /// </summary>
        /// <param name="BestOL"></param>
        /// <param name="BestErr"></param>
        /// <param name="BestWrn"></param>
        /// <param name="BestValidation"></param>
        /// <param name="BestImage"></param>
        /// <param name="Model"></param>
        /// <param name="Db"></param>
        /// <param name="InLine"></param>
        /// <param name="SubElem"></param>     
        /// <param name="BestExp"></param>
        public static void ValidateOneLine(MM_Database_OneLine InLine, MM_Database_Model Model, CIM_Element SubElem, ref MM_Database_OneLine BestOL, ref MM_Oneline_Export BestExp, ref int BestErr, ref int BestWrn, ref string BestValidation, ref Image BestImage, MM_Database_Connector Db)
        {
            int Errors, Warnings;
            MM_Oneline_Export OLExport = new MM_Oneline_Export(InLine, Model);
            //OneLinePreview.AssignOneLine(InLine, OneLineImage, Model.PriorModel, Repository);
            Application.DoEvents();
            String ValidationResult;
            try
            {
                ValidationResult = "\n\t" + MM_OneLine_Validation.ValidateTopology(InLine.Document.DocumentElement, Model, OLExport, out Errors, out Warnings).Replace("\n", "\n\t") + "\n";
            }
            catch (Exception ex)
            {
                ValidationResult = "ERROR: " + ex.Message + "\n\n" + ex.StackTrace;
                Exception ex2 = ex.InnerException;
                while (ex2 != null)
                {
                    ValidationResult += "\n\n" + ex2.Message + "\n\n" + ex2.StackTrace;
                    ex2 = ex2.InnerException;
                }
                Errors = 1000;
                Warnings = 1000;
            }

            //Now, run our comparison
            if (BestOL == null || (Errors < BestErr) || (Errors == BestErr && Warnings < BestWrn))
            {
                BestOL = InLine;
                BestValidation = ValidationResult;
                BestErr = Errors;
                BestWrn = Warnings;
                if (BestImage != null)
                    BestImage.Dispose();
                BestImage = Db.LoadOneLineImage(InLine.ID); 
                BestExp = OLExport;
            }
           
            Console.WriteLine("Validation of {0}: {1} err / {2} wrn: {3}", InLine.Names[0], Errors, Warnings, BestOL == InLine);
        }
    }
}